HGAME 2020 Crypto Writeup

InfantRSA | solved

给了 $p,q$ 直接由 $m\equiv c^{(p-1)(q-1)}(\mod pq)$ 得到明文

hgame{t3Xt6O0k_R5A!!!}

Affine | solved

暴力求得 $A=14,B=13$

hgame{M4th_u5Ed_iN_cRYpt0}

not_One-time | solved

又是道暴力2333

每次可以从 Oracle 得到一个 flag 每个字符异或上一个随机生成的相同长度的 key_stream, 重点在于题目保证 key_stream 的自符只包括大小写和数字。

OTP的话显然没什么好办法,只能每次得到密文后对每一位试可能的字符,如果按此假设的那一位不合法,则排除。无限获取密文,知道每一位上都只剩一种可能性为止,则得到 flag.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import string
from base64 import b64decode
from pwn import *
L = len(b64decode(remote("47.98.192.231",25001).recv()))
psbChar = [[c for c in """!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~"""] for _ in range(L)]
def check():
for i in range(L):
if len(psbChar[i]) != 1:
return False
return True
while not check():
s = b64decode(remote("47.98.192.231",25001).recv())
for i in range(L):
print(i)
for c in psbChar[i]:
if chr(ord(c)^s[i]) not in string.ascii_letters+string.digits:
psbChar[i].remove(c)

ans=''
for i in range(L):
ans += psbChar[i][0];
print(ans)

hgame{r3us1nG+M3$5age-&&~rEduC3d_k3Y-5P4Ce}

Reorder | solved

重排加密,输入一个和 flag 一样长且每个字符不一样的字符串,从此得知重排规则,即可解密。

1
2
3
4
5
6
7
org = "abcdefghijklmnopqrstuvwxyz123456"
srt = "khaifgenjlpcdomb1xqyvwu4z26st53r"
c = "+Uh${jemt5LampIgiT3AmuR!T0}Pe!n_"
ans = ''
for i in range(len(c)):
ans += c[srt.find(org[i])]
print(ans)

hgame{jU$t+5ImpL3_PeRmuTATi0n!!}

Verification_code | solved

暴力

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
from hashlib import sha256
from itertools import product
from functools import reduce
from operator import add
from pwn import *

sh = remote("47.98.192.231",25678)

msg1=str(sh.recv(), encoding="utf8")
print(msg1,end='')
print(str(sh.recv(), encoding="utf8"),end='')

ch = [chr(i) for i in list(range(48,58))+list(range(65,91))+list(range(97,123))]

c1 = msg1[12:28]
c2 = msg1[33:-1]

for s in product(ch, repeat = 4):
if(sha256(bytes(reduce(add, s) + c1, encoding = "utf8")).hexdigest() == c2):
print(reduce(add, s))
sh.sendline(reduce(add, s))
break

print(str(sh.recv(), encoding="utf8"),end='')
print(str(sh.recv(), encoding="utf8"),end='')
print("I like playing Hgame")
sh.sendline("I like playing Hgame")
print(str(sh.recv(), encoding="utf8"),end='')
print(str(sh.recv(), encoding="utf8"),end='')
print(str(sh.recv(), encoding="utf8"),end='')

hgame{It3Rt0O|S+I5_u$3fu1~Fo2_6rUtE-f0Rc3}

Remainder | solved

中国剩余定理,类似 RSA ?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
p = 94598296305713376652540411631949434301396235111673372738276754654188267010805522542068004453137678598891335408170277601381944584279339362056579262308427544671688614923839794522671378559276784734758727213070403838632286280473450086762286706863922968723202830398266220533885129175502142533600559292388005914561
q = 150088216417404963893679242888992998793257903343994792697939121738029477790454833496600101388493792476973514786401036309378542808470513073408894727406158296404360452232777491992630316999043165374635001806841520490997788796152678742544032835808854339130676283497122770901196468323977265095016407164510827505883
r = 145897736096689096151704740327665176308625097484116713780050311198775607465862066406830851710261868913835866335107146242979359964945125214420821146670919741118254402096944139483988745450480989706524191669371208210272907563936516990473246615375022630708213486725809819360033470468293100926616729742277729705727
bp = 78430786011650521224561924814843614294806974988599591058915520397518526296422791089692107488534157589856611229978068659970976374971658909987299759719533519358232180721480719635602515525942678988896727128884803638257227848176298172896155463813264206982505797613067215182849559356336015634543181806296355552543
bq = 49576356423474222188205187306884167620746479677590121213791093908977295803476203510001060180959190917276817541142411523867555147201992480220531431019627681572335103200586388519695931348304970651875582413052411224818844160945410884130575771617919149619341762325633301313732947264125576866033934018462843559419
br = 48131077962649497833189292637861442767562147447040134411078884485513840553188185954383330236190253388937785530658279768620213062244053151614962893628946343595642513870766877810534480536737200302699539396810545420021054225204683428522820350356470883574463849146422150244304147618195613796399010492125383322922
e=65537
from gmpy2 import invert as inv
from binascii import a2b_hex
from Crypto.Util import number
c = (inv(q*r, p) * q*r * bp + inv(p*r, q) * p*r * bq + inv(p*q, r) * p*q * br) % (p * q * r)
d = inv(e, (p - 1) * (q - 1) * (r - 1))
m = pow(c, d, p * q * r)
print(str(number.long_to_bytes(m),encoding='utf-8'))

hgame{CrT_w0Nt+6Oth3R_mE!!!}

Inv | solved

排列换位置,看成乘法,写个逆运算即可。

(正解是扩展欧几里得?无所谓了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
s595 = b'\xc8&\xc1E\xbe*\xc5\x84\xdb1\x05\x9b\xc0\xf2\xac/\x0b0\x8d\'\xc2b\x89\x93\xa6\xcd\xe1\x1b\xf4H\xffa\x90A\xf7,(\xea?\xa8\xa0\x8b\xf1\xf9"X\n\x86fj\x074\x7fBO\xd4F\xbd\xe6\xd9\xa7\xaf\x8a\x8c\xde\xab;!PT\x15)+w\xbc\x00>\xc6g\xc3\x85=9K\xb6<\xb7x\xaeUG\x83vk\xa9\xf6{\x03Y\x87\x14e\xfd\xed@i\xcc.\xd1\xebo\x106\xe2\xe7\xd7\xeeu\x9e\xfe\x95^R\xfb8\x04\xb4S\x16\xe0\xad\xd8\x98n\xca\xe4\xdd\xd2\xc7\x99l\xb3\\2L\xa3\x1d:_\x12\xb87\x17\x01\xb1#~q\x1c\t\xe8\xdar\xef\xcb\x0c\xe5\x80\xdf\xc9y\x0e`\xe9\x94\xd0\xcfW\x1f5\xf5h\xbf\xba\x91\xb9d\xfcM\x81\xec\x88\xb2c\x9f\xa4J|\xd3m\xd6s\xd5\x92\x9d\x9a3\xa2\xb5\xfa\x19N\xa1\x82][\xf8\x06\x13\xdcC\x1e\x1a\xaa\xc4tz\x08\x8f%$D\xbb\x97 \xce\x96V\xe3\x02I\x18\x11\x0f\r\xf3p\x8e\xa5Z-\xf0}\xb0Q\x9c'
s739 = b'U\x17\x8aE\xa6\x19\xab\x7fd0\xd2)\xc0\xae\xcc/G_\xe3\'\r\xfb\xaf\x00\xb1hgi-\xc1\xffa\x8d\t&\x99k\x95\x93\xa8.\x07\xcd\x87\x01\xe8\x89\x86\xf6f48F\xdc\x96\xd4`P\xd6!\xfe\xc4B:\xd31C\x9f\x1dT{2c9\x0bY5#\xf7\xb8H\xe0Db\xb6wv\xe1\xbbI\x8f\x83l\x80\xa9\x04q\x03\xf0m\xf4\x1bp\x8e\xc6u\xfd\x16$\x06\xf9Z\xec\xa2\xcb\xd7V\xb9\xd1\xbdt^\xe7\xe2\xac\x18\xb4\x15=n\xad\xd8S+\xca\xeb\xdd\xd0;\x84\xe6\x08\x8c3\xb3\x90\x02\xc8}\xee\xea7K\x98\xde\x8b~\xcf\xfa\x11\n\xda\xa4L\xa3\x0cWQ\xdf\xc9yj\x9d\xe9\xfcJO\x1a\x1f\xdb\xf5M\xbf\x9e e\x1c*\x9b\x85\xe5\x88\xb2\xc7\xf2\x91\x10\x0e,\xd9<s\xd5\xef\xb0@|\xc3\xbc(\xb5"\xa1\x82\xa7[\xf8A\x13\x14\xc2\x1eN\xaao\xedr\xba\xcex]\x92\x05\x97\x12\xc5%\\\xb7>R\x9a\x94\x0fX\xf3\xbe?\xa5\xe4\xa0z\xf16\x81\x9c'
flag = b"\\-\xa5{\xb9\x85J @\xfa\x91\x0b\x88\r4I\x7f\xb9\xe5\r\xc0\x84\x8f\xa6\xc0i\xb0\xa4\x1b\x8fIw\x84'\xa2\xa4\x00\x91\x87\x10\r\\\x8c\x12"
def Mul(S, X):
return bytes([ S[x] for x in X ])

def Pow(X, E):
Y = X
E = bin(E)[3:]
for e in E:
Y = Mul(Y, Y)
if e=='1':
Y = Mul(X, Y)
return Y

def Dvd(a,b):
return bytes([a.find(i) for i in b])

s144=Dvd(s595,s739)
s19=Dvd(Pow(s144,4),s595)
s11=Dvd(Pow(s19,7),s144)
s=Dvd(Pow(s11,13),s144)
print(str(Dvd(s,flag),encoding='utf-8'))

hgame{U_kN0w~tH3+eXtEnD-EuC1iD34n^A1G0rIthM}

notRC4 | solved

RC4密码,给出了乱序的 Sbox ,穷举 j 还原出原先的 Sbox 即可解密。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
c = b'ry\x80\xe3*\xb1\x04m\r\x12R\xeaW\x80\xba\xf7\xc5\x02g8Z\xfe\xb8f\xf5\xdc\xec\xdb\xe6\xe6\xa6\xc61\x92\xeb6K\xa1"\x01A\xfbP\xf1P\xf0\x8dQ\x81~'
for init_j in range(256):
Sbox = [40, 226, 202, 106, 96, 6, 208, 142, 83, 90, 37, 217, 149, 30, 157, 238, 71, 114, 117, 190, 76, 154, 135, 201, 221, 35, 245, 239, 146, 153, 33, 212, 123, 244, 178, 89, 145, 189, 183, 227, 171, 198, 34, 182, 205, 158, 194, 133, 126, 251, 195, 88, 206, 15, 161, 70, 82, 64, 170, 86, 131, 147, 36, 39, 47, 235, 172, 12, 228, 78, 116, 118, 50, 9, 1, 23, 169, 81, 53, 98, 218, 237, 152, 159, 139, 121, 61, 250, 124, 151, 211, 187, 141, 49, 43, 128, 137, 162, 233, 200, 188, 11, 109, 110, 254, 234, 253, 180, 69, 247, 42, 115, 155, 193, 209, 199, 32, 130, 111, 225, 204, 164, 101, 249, 236, 20, 224, 191, 166, 160, 17, 16, 68, 52, 192, 207, 104, 144, 26, 72, 67, 44, 246, 184, 74, 10, 197, 93, 181, 243, 112, 5, 94, 8, 148, 223, 210, 143, 56, 73, 103, 129, 77, 196, 92, 120, 175, 63, 168, 22, 62, 174, 127, 215, 65, 51, 220, 41, 179, 2, 167, 125, 7, 29, 21, 66, 14, 24, 105, 113, 214, 99, 231, 255, 84, 85, 138, 242, 91, 150, 25, 222, 156, 248, 48, 108, 252, 79, 107, 230, 38, 55, 132, 54, 186, 45, 173, 80, 134, 232, 60, 240, 4, 102, 95, 122, 18, 136, 229, 140, 3, 216, 100, 87, 75, 0, 31, 185, 28, 97, 46, 241, 219, 213, 59, 177, 57, 203, 163, 165, 19, 176, 13, 119, 27, 58]
i = len(c)
j = init_j
for _ in range(len(c)):
Sbox[i], Sbox[j] = Sbox[j], Sbox[i]
j = (j - Sbox[i]) % 256
i -= 1
i = j = 0
k = []
for _ in range(len(c)):
i += 1
j = (j + Sbox[i]) % 256
Sbox[i], Sbox[j] = Sbox[j], Sbox[i]
t = (Sbox[i] + Sbox[j]) % 256
k.append(Sbox[t])
if Sbox==[40, 226, 202, 106, 96, 6, 208, 142, 83, 90, 37, 217, 149, 30, 157, 238, 71, 114, 117, 190, 76, 154, 135, 201, 221, 35, 245, 239, 146, 153, 33, 212, 123, 244, 178, 89, 145, 189, 183, 227, 171, 198, 34, 182, 205, 158, 194, 133, 126, 251, 195, 88, 206, 15, 161, 70, 82, 64, 170, 86, 131, 147, 36, 39, 47, 235, 172, 12, 228, 78, 116, 118, 50, 9, 1, 23, 169, 81, 53, 98, 218, 237, 152, 159, 139, 121, 61, 250, 124, 151, 211, 187, 141, 49, 43, 128, 137, 162, 233, 200, 188, 11, 109, 110, 254, 234, 253, 180, 69, 247, 42, 115, 155, 193, 209, 199, 32, 130, 111, 225, 204, 164, 101, 249, 236, 20, 224, 191, 166, 160, 17, 16, 68, 52, 192, 207, 104, 144, 26, 72, 67, 44, 246, 184, 74, 10, 197, 93, 181, 243, 112, 5, 94, 8, 148, 223, 210, 143, 56, 73, 103, 129, 77, 196, 92, 120, 175, 63, 168, 22, 62, 174, 127, 215, 65, 51, 220, 41, 179, 2, 167, 125, 7, 29, 21, 66, 14, 24, 105, 113, 214, 99, 231, 255, 84, 85, 138, 242, 91, 150, 25, 222, 156, 248, 48, 108, 252, 79, 107, 230, 38, 55, 132, 54, 186, 45, 173, 80, 134, 232, 60, 240, 4, 102, 95, 122, 18, 136, 229, 140, 3, 216, 100, 87, 75, 0, 31, 185, 28, 97, 46, 241, 219, 213,gg 59, 177, 57, 203, 163, 165, 19, 176, 13, 119, 27, 58]:
m = ''.join([chr(ord(c[__])^k[__]) for __ in range(len(c))])
print(m)
break

hgame{Oooooo00__reVEr$e~THE~PrGa+OF+rC4++OO000O0O}

RSA? | solved

本质签到题,难度全靠骗

发现 $n$ 是质数,且 $n\equiv3(\mod4)$,直接用结论:
若 $m^2\equiv c (\mod n)$, 则 $m\equiv c^\frac{n+1}{4}(\mod n)$

想了很久 $q$ 怎么用,结果发现给出 $q$ 只是为了唬人

1
2
3
4
5
6
7
from Crypto.Util.number import long_to_bytes
c = 2166906408965390116437761185603075699086315246646982132520792868301615462732707695943058783211233589602162482847874299962577512765886861573898075548033678
e = 2
q = 95476159140358852660572143425801843414366266400633576268276298731539374363607
n = 7236834786093916009325417235344284284391050287273422058348241360770131423240807259717864686885012301293921328397391157761688776563780348734483657156421779
m = pow(c, (n + 1) // 4, n)
print(str(long_to_bytes(m),encoding='utf-8'))

hgame{eaa5262c-4631-46ef-a97b-53277ab7e1d8}

ToyCipher_XorShift | solved

难得拿了二血,可能大家被分值吓到了?

本质码农模拟题。XorShift密码本身就是可逆的,逆着写就好了……

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
from Crypto.Util.number import long_to_bytes

BLOCKSIZE = 8
BITSLENGTH = 8*BLOCKSIZE
MASK = (1 << BITSLENGTH) - 1

BLOCKS = lambda data: [ bytes(data[i*BLOCKSIZE:(i+1)*BLOCKSIZE]) for i in range(len(data)//BLOCKSIZE) ]
XOR = lambda s1, s2: bytes([x^y for x,y in zip(s1, s2)])

def binget(x, h, l):
return x & (((1 << h) - 1) ^ ((1 << l) - 1))

def g(x, a, shr=True):
x = x & MASK
a = a % BITSLENGTH
if shr:
for i in range(BITSLENGTH // a):
x ^= binget(x, BITSLENGTH - i * a, BITSLENGTH - (i + 1) * a) >> a
else:
for i in range(BITSLENGTH // a):
x ^= binget(x, (i + 1) * a, i * a) << a
return x & MASK

def dec(block):
block = int.from_bytes(block, byteorder='big')
block = g(block, 17, shr=False)
block = g(block, 7, shr=True )
block = g(block, 13, shr=False)
return block.to_bytes(BLOCKSIZE, byteorder='big')

def decrypt(cipher, iv, unpad=False):
assert len(cipher)%BLOCKSIZE == 0
mid = iv
msg = b''
for block in BLOCKS(cipher):
msg += XOR(dec(block), mid)
mid = block
return msg

IV = b'c8C~M0d3'
cipher = 0x15eb80358fe6f89b1802a5f3eb5a6ec6c33dc4f35822fb6e97e0b22be860a28602b35e2930a93ac5
print(decrypt(long_to_bytes(cipher),IV))

hgame{tHi$+4lgOr1thM_i5_3@sY-t0~b2EaK}

Exchange | solved

好恶心的题,调了一下午

数学上不难,在双方交换 $A,B$ 时向双方分别发送错误的 $A’=B’=g^2$

这样双方计算得到的 $S_a=A^2,S_b=B^2$

那么我们得知 $C_b’$ 后即可计算 $m_b\equiv C_b’B^{-2}(\mod p)$

需要注意的是Alice若收到错误的信息就不会发出 $C_a’$ 了, 因此需要将 Bob 发给她的信息改为 $C_b’’=m_bA^2\mod p$, 这样得到 $C_a$ 后即可计算 $m_a\equiv C_a’A^{-2}(\mod p)$

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
from hashlib import sha256
from itertools import product
from functools import reduce
from operator import add
from pwn import *
from gmpy2 import invert as inv
from Crypto.Util.number import long_to_bytes

sh = remote("47.98.192.231",25258)
msg1=str(sh.recv(), encoding="utf8")
print(msg1,end='')
print(str(sh.recv(), encoding="utf8"),end='')
ch = [chr(i) for i in list(range(48,58))+list(range(65,91))+list(range(97,123))]
c1 = msg1[12:28]
c2 = msg1[33:-1]
for s in product(ch, repeat = 4):
if(sha256(bytes(reduce(add, s) + c1, encoding = "utf8")).hexdigest() == c2):
print(reduce(add, s))
sh.sendline(reduce(add, s))
break

print(str(sh.recv(), encoding="utf8"),end='')
print(str(sh.recv(), encoding="utf8"),end='')
print("")
sh.sendline("")
print(str(sh.recv(), encoding="utf8"),end='')
msg2 = sh.recv()
print(str(msg2, encoding="utf8"),end='')
p = int(msg2[msg2.find(b'Alice: p = ') + 11:msg2.find(b'Alice: g = ') - 1])
g = int(msg2[msg2.find(b'Alice: g = ') + 11:])
print("")
sh.sendline("")
print(str(sh.recv(), encoding="utf8"),end='')
print(str(sh.recv(), encoding="utf8"),end='')
print("")
sh.sendline("")
print(str(sh.recv(), encoding="utf8"),end='')
msg3 = sh.recv()
print(str(msg3, encoding="utf8"),end='')
A = int(msg3[msg3.find(b'A = ') + 4:msg3.find(b'[WARNING] : Do you want to modify this message? (yes/no)')])
print("yes")
sh.sendline("yes")
print(str(sh.recv(), encoding="utf8"),end='')
print(str(sh.recv(), encoding="utf8"),end='')
print(pow(g,2,p))
sh.sendline(str(pow(g,2,p)))
print(str(sh.recv(), encoding="utf8"),end='')
print(str(sh.recv(), encoding="utf8"),end='')
print("")
sh.sendline("")
print(str(sh.recv(), encoding="utf8"),end='')
msg4 = sh.recv()
print(str(msg4, encoding="utf8"),end='')
B = int(msg4[msg4.find(b'B = ') + 4:msg4.find(b'[WARNING] : Do you want to modify this message? (yes/no)')])
print("yes")
sh.sendline("yes")
print(str(sh.recv(), encoding="utf8"),end='')
print(str(sh.recv(), encoding="utf8"),end='')
print(pow(g,2,p))
sh.sendline(str(pow(g,2,p)))
print(str(sh.recv(), encoding="utf8"),end='')
print(str(sh.recv(), encoding="utf8"),end='')
print("")
sh.sendline("")
print(str(sh.recv(), encoding="utf8"),end='')
print(str(sh.recv(), encoding="utf8"),end='')
print("")
sh.sendline("")
print(str(sh.recv(), encoding="utf8"),end='')
print(str(sh.recv(), encoding="utf8"),end='')
print("")
sh.sendline("")
print(str(sh.recv(), encoding="utf8"),end='')
print(str(sh.recv(), encoding="utf8"),end='')
print("")
sh.sendline("")
print(str(sh.recv(), encoding="utf8"),end='')
msg5 = sh.recv()
print(str(msg5, encoding="utf8"),end='')
C_b = int(msg5[msg5.find(b'C_b = ') + 6:msg5.find(b'[WARNING] : Do you want to modify this message? (yes/no)')])
m_b = ( C_b * inv( pow(B, 2, p) , p) ) % p
print("yes")
sh.sendline("yes")
print(str(sh.recv(), encoding="utf8"),end='')
print(str(sh.recv(), encoding="utf8"),end='')
print( ( m_b * pow(A, 2, p) ) % p)
sh.sendline( str( ( m_b * pow(A, 2, p) ) % p ) )
print(str(sh.recv(), encoding="utf8"),end='')
msg6 = sh.recv()
print(str(msg6, encoding="utf8"),end='')
C_a = int(msg6[msg6.find(b'C_a = ') + 6:])
m_a = ( C_a * inv( pow(A, 2, p) , p) ) % p
print("")
sh.sendline("")
print(str(sh.recv(), encoding="utf8"),end='')
flag = long_to_bytes(m_a) + long_to_bytes(m_b)
print(str(flag, encoding='utf-8'))

hgame{Wow!+U_d0_tH3_m@N-1n~ThE+miDd!3_4TtAck~}

Feedback | solved

这题思维难度确实挺大的,想了一天了才恍然大悟QwQ

第一次连接服务器:由于 CFB 的特点,$K_1$ 是可以直接通过 $c_1\oplus m_1$ 得到的,这样得到 $M_1=C_1\oplus K_1$

第二次连接服务器:首先用前述方法求出此次连接的 $K_1$. 对于 $K_2$ 不同的明文得出的就不一样了,所以需要取 $c_2=\text{encrypt}(M_1)+\text{urandom(16)}$ ,这样就可以得到 $C_1$ 所对应的 $K_2=c_2[16:]\oplus m_2[16:]$, 并得到 $M_2=C_2\oplus K_2$

第三次连接服务器:同理,求出 $K3,M3$

下面贴的脚本是第三次连接服务器的脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import os, random
from Crypto.Util.number import bytes_to_long, long_to_bytes
from pwn import *
XOR = lambda s1, s2: bytes([x^y for x,y in zip(s1, s2)])

sh = remote("47.98.192.231", 25147)

print(str(sh.recv(),encoding='utf-8'),end='')

print(str(sh.recv(),encoding='utf-8'),end='')
c1 = os.urandom(16)
print(hex(bytes_to_long(c1))[2:])
sh.sendline(hex(bytes_to_long(c1))[2:])
msg1 = sh.recv()
print(str(msg1,encoding='utf-8'),end='')
m1 = long_to_bytes(int(b'0x'+msg1, 16))
K1 = XOR(c1, m1)

print(str(sh.recv(),encoding='utf-8'),end='')
c2 = XOR(b'FLAG is hgame{51', K1) + os.urandom(16)
print(hex(bytes_to_long(c2))[2:])
sh.sendline(hex(bytes_to_long(c2))[2:])
msg2 = sh.recv()
print(str(msg2,encoding='utf-8'),end='')
m2 = long_to_bytes(int(b'0x'+msg2, 16))
K2 = XOR(c2[16:], m2[16:])

print(str(sh.recv(),encoding='utf-8'),end='')
c3 = XOR(b'b72d4cd23b2fe672', K2) + os.urandom(16)
print(hex(bytes_to_long(c3))[2:])
sh.sendline(hex(bytes_to_long(c3))[2:])
msg3 = sh.recv()
msg4 = msg3[msg3.find(b'\n') + 1:]
msg3 = msg3[:msg3.find(b'\n')]
print(str(msg3,encoding='utf-8'),end='')
m3 = long_to_bytes(int(b'0x'+msg3, 16))
K3 = XOR(c3[16:], m3[16:])

print(str(msg4,encoding='utf-8'),end='')
flag = long_to_bytes(int(b'0x'+msg4[34:], 16))
print(str(XOR(flag, K1 + K2 + K3),encoding='utf-8'))

hgame{51b72d4cd23b2fe672a874cb44020868}

CBCBC | solved

分析加密流程发现:往密文上第 $i$ 个 byte 异或一个数,则解密出的明文上第 $i+32$ 个 byte 也异或上该数。用此方法可以得到一切想要的明文的密文。

Oracle 提供一个检测密文 padding 是否合法的方法,因此从后往前对每一个 byte, 猜测该 byte 的值,将该 byte 及之后的都填充成相应的 padding,如果猜对了则解密出来的明文 padding 合法,从而猜出明文的所有 byte。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
import os, random
from hashlib import sha256
from itertools import product
from functools import reduce
from operator import add
from pwn import *
from math import gcd
from gmpy2 import invert as inv
from Crypto.Util.number import bytes_to_long, long_to_bytes
from pwn import *
BLOCKS = lambda data: [data[16*i:16*(i+1)] for i in range(len(data)//16)]
XOR = lambda s1, s2: bytes([x^y for x,y in zip(s1, s2)])
BLOCKSIZE = 16
def pad(data):
pad_len = BLOCKSIZE - (len(data) % BLOCKSIZE)
return data + bytes( [pad_len] * pad_len )

def unpad(data):
pad_len = data[-1]
_data = data[:-pad_len]
if pad(_data) != data:
raise ValueError('Padding is incorrect.')
return _data

sh = remote("47.98.192.231", 25371)
msg1=str(sh.recv(), encoding="utf8")
print(msg1,end='')
print(str(sh.recv(), encoding="utf8"),end='')
ch = [chr(i) for i in list(range(48,58))+list(range(65,91))+list(range(97,123))]
c1 = msg1[12:28]
c2 = msg1[33:-1]
for s in product(ch, repeat = 4):
if(sha256(bytes(reduce(add, s) + c1, encoding = "utf8")).hexdigest() == c2):
print(reduce(add, s))
sh.sendline(reduce(add, s))
break

print(str(sh.recv(), encoding="utf8"),end='')
msg2 = sh.recv()
print(str(msg2, encoding="utf8"),end='')
flag_cipher = long_to_bytes(int(b'0x'+msg2[:msg2.find(b'\n')], 16))

CHARSET = """0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz{}"""

flag = [0]*60+[4]*4

for i in range(59,-1,-1):
pad_len = 16 - i % 16
for c in CHARSET:
cipher = flag_cipher[:i]+XOR(flag_cipher[i:i+pad_len],XOR(bytes([ord(c)]+flag[i+1:]),bytes([pad_len]*pad_len)))+flag_cipher[i+pad_len:i+pad_len+32]
sh.sendline(hex(bytes_to_long(cipher))[2:])
msg = sh.recv()
if msg[0]==100:
flag[i] = ord(c)
sh.recv()
break
sh.recv()
print(unpad(bytes(flag)))

hgame{I_like_Padding_oracle_attack_6f64ab73204ff3fe89fd90a2}

ToyCipher_Linear | solved

设 $F(x)=x<<<7\oplus x<<<2$

易证 $F$ 不可逆,但注意到 $F(a\oplus b)=F(a)\oplus F(b)$

则 $c=\sigma(m)\oplus \mu(\text{key})$

那么对于相同的 key, 每一块的 $\sigma(m)\oplus c=\mu(\text{key})$ 为定值, 而 $\sigma(m)$ 即为 key 为 0 时的密文。

可以求出该定值,这样再将 flag 的密文分块异或上定值得到 key 为 0 时的密文,再调用 decrypt 函数解密即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os, binascii
XOR = lambda s1, s2: bytes([x^y for x,y in zip(s1, s2)])
#from secret import flag

def rotL(x, nbits, lbits):
mask = 2**nbits - 1
return (x << lbits%nbits) & mask | ( (x & mask) >> (-lbits % nbits) )


def rotR(x, nbits, rbits):
return rotL(x, nbits, -rbits%nbits)


def keySchedule(masterkey):
roundKeys = [ ( rotR(masterkey, 64, 16*i) % 2**16 ) for i in range(12) ]
return roundKeys


def f(x, roundkey):
return rotL(x, 16, 7) ^ rotL(x, 16, 2) ^ roundkey


def ToyCipher(block, mode='enc'):
'''Feistel networks'''
roundKeys_ = ROUNDKEYS
if mode == 'dec':
roundKeys_ = roundKeys_[::-1]

L, R = (block >> 16), (block % 2**16)
for i in range(12):
_R = R
R = L ^ f( R, roundKeys_[i] )
L = _R

return (R << 16) | L


def pad(data, blocksize):
pad_len = blocksize - (len(data) % blocksize)
return data + bytes( [pad_len] * pad_len )


def unpad(data, blocksize):
pad_len = data[-1]
_data = data[:-pad_len]
assert pad(_data, blocksize)==data, "Invalid padding."
return _data


def encrypt(plaintext):
'''ECB mode'''
plaintext = pad(plaintext, BLOCKSIZE)
ciphertext = b''
for i in range( len(plaintext) // BLOCKSIZE ):
block = plaintext[i*BLOCKSIZE:(i+1)*BLOCKSIZE]
block = int.from_bytes(block, byteorder='big')
E_block = ToyCipher(block)
ciphertext += E_block.to_bytes(BLOCKSIZE, byteorder='big')
return ciphertext


def decrypt(ciphertext):
'''ECB mode'''
plaintext = b''
for i in range( len(ciphertext) // BLOCKSIZE ):
block = ciphertext[i*BLOCKSIZE:(i+1)*BLOCKSIZE]
block = int.from_bytes(block, byteorder='big')
D_block = ToyCipher(block, 'dec')
plaintext += D_block.to_bytes(BLOCKSIZE, byteorder='big')
plaintext = unpad(plaintext, BLOCKSIZE)
return plaintext


masterkey = b'\0'*8
masterkey = int.from_bytes(masterkey, byteorder='big')
ROUNDKEYS = keySchedule(masterkey)
BLOCKSIZE = 4

c0 = encrypt(b'just a test')
c1 = b'\x91a\xb1o\xed_\xb2\x8c\x00\x1b\xdfp'
C = XOR(c0,c1)[:4]
flag1 = b'\xe6\xf9\xda\xf0\xe18\xbc\xb4[\xfb\xbe\xd1\xfe\xa2\t\x8d\xdft:\xee\x1f\x1d\xe2q\xe5\x92/$#DL\x00\x1dD5@\x01W?!7CQ\xc16V\xb0\x14q)\xaa2'
flag0 = b''
for i in range(len(flag1)//BLOCKSIZE):
flag0 += XOR(C, flag1[i*BLOCKSIZE:(i+1)*BLOCKSIZE])
print(decrypt(flag0))

hgame{r0TAT!on_&&-x0r 4Re-b0tH~l1neaR_0pEr4t1On5}